/**
 * @file array.h
 * @author Toni Uhlig
 * @date 4 Oct 2014
 * @brief Defines data structures and algorithms for arrays.
 *
 * This defines the essential array data structure and all important helper functions.
 */

#ifndef ARRAY_H
#define ARRAY_H 1

#include <stdio.h>
#include <stdint.h>

#include "main.h"

struct __ncattr__ array {
  size_t used;             /**< The quantity of used array elements. */
  size_t size;             /**< The maximum of storable array elements, before the array has to be resized */
  uint8_t type_size;       /**< The exact size in bytes which each array element consumes. */
  uint8_t next_power_of_2; /**< Defines the next maximum of storable array elements if the array has to be resized (exponent to base two) */
  void *data;              /**< The pointer to the first element of the array. */
};

/**
 * @brief Reserves the memory for the internal data structure (#array) and its initial elements.
 * @param type_size Describes the size of each element. (#array.type_size)
 * @param value_power_of_2 The initial quantity of maximum storable elements as exponent to base two. (#array.next_power_of_2)
 * @return Returns a pointer to an #array data structure or NULL if something went wrong.
 */
struct array *
array_create(size_t type_size, uint8_t value_power_of_2);

/**
 * @brief Sets an #array to the new size.
 *
 * Remember: It decreases the size of the field, every element between the old and new size will be lost.
 * @param array_ptr The pointer to the #array struct.
 * @param new_size The new size of the #array.
 * @return Returns the same pointer as given as second parameter. (might change to void in the future)
 */
struct array *
array_realloc(struct array *array_ptr, size_t new_size);

/**
 * @brief Free every element with typesize #array.type_size in the field and free the data structure itself.
 *
 * Remember: If you are storing pointers in the field, dont forget to free(...) before array_free(...)
 * @param arr The pointer to the #array struct.
 */
void
array_free(struct array *arr);

/**
 * @brief Sets element(s) in the field directly to data offset and with specified element count.
 * @param array_ptr The field pointer.
 * @param data Offset of the data.
 * @param data_len Quantity of the following elements after the offset.
 */
void
array_set_data(struct array *array_ptr, const void *data, size_t data_len);

/**
 * @brief Same as #array_set_data with one difference. It adds instead of setting the specified offset to the end of the field.
 * @param array_ptr The field pointer.
 * @param data Offset of the data.
 * @param data_len Quantity of the following elements after the offset.
 */
void
array_add_data(struct array *array_ptr, const void *data, size_t data_len);

/**
 * @brief Removes one or more elements from the field.
 * @param array_ptr The field pointer.
 * @param index Index of the item.
 * @param len Quantity of items after index.
 */
void
array_delete_data(struct array *array_ptr, size_t index, size_t len);

/**
 * @brief Similar to #array_set_data/#array_add_data. Inserts data to a specific index in the field.
 * @param array_ptr The field pointer.
 * @param index Index of the desired position in the array.
 * @param data Offset of the data to insert.
 * @param data_len Quantity of the following elements after the offset.
 */
void
array_insert_data(struct array *array_ptr, size_t index, const void *data, size_t data_len);

/**
 * @brief Calculate the absolute position of an element in the field.
 * @param array_ptr The field pointer.
 * @param index Index of the element in the field.
 * @return The absolute position.
 */
size_t
array_idx_abs(struct array *array_ptr, size_t index);

/**
 * @brief Calculate the absolute size of all used elements in the field.
 *
 * Basically it returns #array.type_size multiplied with #array.used
 * @param array_ptr The field pointer.
 * @return The absolute size of all used elements.
 */
size_t
array_used_abs(struct array *array_ptr);

/**
 * @brief Calculate the absolute size of the field.
 *
 * This includes occupied elements and non-occupied elements.
 * @param array_ptr The field pointer.
 * @return The absolute size of the field.
 */
size_t
array_size_abs(struct array *array_ptr);

/**
 * @brief Get the offset of a specified element.
 * @param array_ptr The field pointer.
 * @param index The index of the element.
 * @return Pointer to the offset or NULL if the index is invalid.
 */
const void *
array_get_offset(struct array *array_ptr, size_t index);

/**
 * @brief Get the offset of the last element.
 * @param array_ptr The field pointer.
 * @return Offset of the last element.
 */
const void *
array_last(struct array *array_ptr);

#ifdef ENABLE_DEBUG
/**
 * @brief Print debugging stuff related to the underlying data structure (#array).
 *
 * This is only available if the macro ENABLE_DEBUG is defined!
 * Prints text using the DD macro.
 * @param array_ptr The target pointer to the field
 */
void
array_print_debug(struct array *array_ptr);
#endif

#endif
